package sim32;


import java.io.*;
import java.util.Scanner;

/**
 * The main simulator class invoked from the command line.
 * @author Piotr
 * 
 */
public class Sim {

	static Pipeline pipe;
	
	static final int maxCycleCount = 10000;
	static final int numberOfPipes = 2;
	
	/**
	 * Main method. Constructs all necessary objects and controls execution.
	 * @param args	Filename of program to be run in the simulator. Optional filename
	 * 				of data memory initialization file.
	 */
	public static void main(String[] args) throws IOException, InvalidInstructionException {
		
		
		File programFile;
		File dataFile = null;
		
		if (args.length > 0) {
			programFile = new File(args[0]);
			if (!programFile.exists()) {
				System.out.println("File \"" + args[0] + "\" does not exist");
				System.exit(1);
			}
			if (args.length > 1) {
				dataFile = new File(args[1]);
				if (!dataFile.exists()) {
					System.out.println("File \"" + args[0] + "\" does not exist");
					System.exit(1);
				}
			}
			// Create the pipeline objects
			
			if (dataFile != null) {
				pipe = new Pipeline(numberOfPipes, programFile, dataFile);
			} else {
				pipe = new Pipeline(numberOfPipes, programFile);
			}
			int instrsInPipe = 0;
			int cycleCount = 0;
			int instrCount = 0;
			boolean programDone = false;
			
			//Main program loop
			while(!programDone && cycleCount < maxCycleCount) {
				instrsInPipe = 0;
				cycleCount++;
				instrsInPipe = pipe.cycle();
				for (int ii = 0; ii < numberOfPipes; ii++) {
					Instruction wbInstr = pipe.wbs[ii].getInstruction();
					if (wbInstr != null) {
						String tag = wbInstr.textString;
						printOutput(tag, cycleCount, instrsInPipe, ii);
					}
				}
				//printFullPipeState(cycleCount, instrsInPipe);
				instrCount += pipe.numWBInstrs();
				if (pipe.ifs.isProgramFinished() && instrsInPipe == 0) {
					programDone = true;
					System.out.println("###################### Program Completed ######################");
					System.out.println("Statistics:");
					System.out.println("Total instructions executed: " + instrCount);
					System.out.println("Total clock cycles: " + (cycleCount - 1));
					System.out.println("Average CPI: " + (float)(cycleCount - 1)/instrCount);
					System.out.println(pipe.ids.dumpRegisters());
					System.out.println(pipe.dumpMemory());
				}
				
			}
			if (cycleCount >= maxCycleCount) {
				System.out.println("Max cycle limit (" + maxCycleCount + ") reached.");
			}

		} else {
			System.out.println("No input file specified");
			System.exit(1);
		}
				

	}
	
	/**
	 * Method used to print out the pipeline state. Based on the tags appended to the
	 * instructions in out intermediate files.
	 * @param tag
	 * @param cycleCount
	 * @param instrsInPipe
	 * @param pipeID
	 */
	static private void printOutput(String tag, int cycleCount, int instrsInPipe, int pipeID) {
		//System.out.println(tag);
		Scanner sc = new Scanner(tag);
		String match = sc.findInLine("^![ipmr]+");
		if (match != null) {
			//System.out.println(match);
			sc = new Scanner(match);
			System.out.println("####################### Cycle " + cycleCount + "\t#######################");
			if (match.contains("p")) {
				for (int ii = 0; ii < numberOfPipes; ii++) {
					System.out.println("*************** IF" + ii + " Stage Instruction: ***************");
					System.out.println(pipe.ifs.getInstruction(ii));
				}
				System.out.println("==ID Stats: InsCnt: " + pipe.ids.instrCount + " ReadyCnt: "
						+ pipe.ids.readyCount + " FetchCnt: " + pipe.ids.fetchCnt + " ==");
				for (int ii = 0; ii < numberOfPipes*2; ii++) {
					System.out.println("*************** ID" + ii + " Stage Instruction: ***************");
					System.out.println(pipe.ids.getInstruction(ii));
				}
				for (int ii = 0; ii < numberOfPipes; ii++) {
					System.out.println("*************** EX" + ii + " Stage Instruction: ***************");
					System.out.println(pipe.exs[ii].getInstruction());
				}
				for (int ii = 0; ii < numberOfPipes; ii++) {
					System.out.println("*************** MEM" + ii + " Stage Instruction: **************");
					System.out.println(pipe.mems[ii].getInstruction());
				}
				for (int ii = 0; ii < numberOfPipes; ii++) {
					System.out.println("*************** WB" + ii + " Stage Instruction: ***************");
					System.out.println(pipe.wbs[ii].getInstruction());
				}
				System.out.println("Instructions in pipeline: " + instrsInPipe);
			} else if (match.contains("i")) {
				System.out.println("*************** WB" + pipeID + " Stage Instruction: ***************");
				System.out.println(pipe.wbs[pipeID].getInstruction());
			}
			if (match.contains("r")) {
				System.out.println(pipe.ids.dumpRegisters());
			}
			if (match.contains("m")) {
				System.out.println(pipe.dumpMemory());
			}
		}
	}
	
	/**
	 * Method used to print out the pipeline state. Used for simulator debugging.
	 * @param cycleCount
	 * @param instrsInPipe
	 */
	static private void printFullPipeState(int cycleCount, int instrsInPipe) {
		System.out.println("####################### Cycle " + cycleCount + "\t#######################");
		for (int ii = 0; ii < numberOfPipes; ii++) {
			System.out.println("*************** IF" + ii + " Stage Instruction: ***************");
			System.out.println(pipe.ifs.getInstruction(ii));
		}
		System.out.println("==ID Stats: InsCnt: " + pipe.ids.instrCount + " ReadyCnt: "
				+ pipe.ids.readyCount + " FetchCnt: " + pipe.ids.fetchCnt
				+ " NextFetch: " + pipe.ids.nextFetchCnt + " ==");
		for (int ii = 0; ii < numberOfPipes*2; ii++) {
			System.out.println("*************** ID" + ii + " Stage Instruction: ***************");
			System.out.println(pipe.ids.getInstruction(ii));
		}
		for (int ii = 0; ii < numberOfPipes; ii++) {
			System.out.println("*************** EX" + ii + " Stage Instruction: ***************");
			System.out.println(pipe.exs[ii].getInstruction());
		}
		for (int ii = 0; ii < numberOfPipes; ii++) {
			System.out.println("*************** MEM" + ii + " Stage Instruction: **************");
			System.out.println(pipe.mems[ii].getInstruction());
		}
		for (int ii = 0; ii < numberOfPipes; ii++) {
			System.out.println("*************** WB" + ii + " Stage Instruction: ***************");
			System.out.println(pipe.wbs[ii].getInstruction());
		}
		System.out.println("Instructions in pipeline: " + instrsInPipe);
	}

}
